package cn.tang.tframe.dubbox.optimizer;

import cn.tang.tframe.common.utils.lang.CollectionUtils;
import cn.tang.tframe.common.utils.lang.StringUtils;
import com.alibaba.dubbo.common.serialize.support.SerializationOptimizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

/**
 * Created by tanghc on 17/3/1.
 */
public abstract class AbstractSerialOptimizer implements SerializationOptimizer {
	protected Logger logger = LoggerFactory.getLogger(this.getClass());
	private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
	final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
	private List<OptimizerBeanFilter> includeFilters;
	private List<OptimizerBeanFilter> excludeFilters;
	private final ResourceLoader resourceLoader = new DefaultResourceLoader();
	private final SimpleMetadataReaderFactory simpleMetadataReaderFactory = new SimpleMetadataReaderFactory(resourceLoader);

	@Override
	public Collection<Class> getSerializableClasses() {
		List<Class> classes = new LinkedList<Class>();
		String scanPath = this.getScanPath();
		if(StringUtils.isNotBlank(scanPath)){
			String[] basePcks = scanPath.split(",");
			if(null != basePcks){
				for(String basePck: basePcks){
					List<Class> classList = scanClass(basePck);
					if(null != classList){
						classes.addAll(classList);
					}
				}
			}
		}
		return classes;
	}
	public List<Class> scanClass(String basePackage){
		List<Class> classes = new LinkedList<Class>();
		try {
			List<Resource> resources = scan(basePackage);
			if(CollectionUtils.isNotEmpty(resources)){
				for(Resource resource: resources){
					try {
						MetadataReader metadataReader = simpleMetadataReaderFactory.getMetadataReader(resource);
						classes.add(Class.forName(metadataReader.getClassMetadata().getClassName()));
					} catch (Exception e) {
						logger.warn("SerializationOptimizer instant class exception: {}", resource,e);
					}
				}
			}
		} catch (Exception e) {
			logger.error("SerializationOptimizer getSerializableClasses exception",e);
		}
		return classes;
	}
	public abstract String getScanPath();
	public List<Resource> scan(String basePackage) throws IOException {
		if(StringUtils.isBlank(basePackage)){
			return null;
		}
		String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
				 basePackage + "/" + DEFAULT_RESOURCE_PATTERN;
		Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
		List<Resource> list = null;
		if(null != resources && resources.length > 0){
			list = new ArrayList<>();
			for(Resource resource: resources){
				if(filterResource(resource)){
					list.add(resource);
				}
			}
		}
		return list;
	}

	/**
	 * 白名单优先,如果在白名单,则通过;如果在黑名单,则拒绝
	 * 如果设置了白名单和黑名单,不在白也不在黑,则通过。
	 * 如果只设置了白名单,不在白名单中拒绝
	 * 如果值设置了黑名单,不在黑名单中通过
	 * 如果都没有设置,全部通过
	 * @param resource
	 * @return
	 */
	public boolean filterResource(Resource resource){
		Boolean onlyWhite = null;
		List<OptimizerBeanFilter> filters = this.getIncludeFilters();
		if(CollectionUtils.isNotEmpty(this.getIncludeFilters())){
			for(OptimizerBeanFilter filter: filters){
				if(filter.filter(resource)){
					return true;
				}
			}
			onlyWhite = true;
		}
		if(CollectionUtils.isNotEmpty(this.getExcludeFilters())){
			onlyWhite = false;
			for(OptimizerBeanFilter filter: this.getExcludeFilters()){
				if(filter.filter(resource)){
					return false;
				}
			}

		}
		if(null != onlyWhite && onlyWhite){
			return false;
		}
		return true;
	}

	public List<OptimizerBeanFilter> getIncludeFilters() {
		return includeFilters;
	}

	public void setIncludeFilters(List<OptimizerBeanFilter> includeFilters) {
		this.includeFilters = includeFilters;
	}

	public List<OptimizerBeanFilter> getExcludeFilters() {
		return excludeFilters;
	}

	public void setExcludeFilters(List<OptimizerBeanFilter> excludeFilters) {
		this.excludeFilters = excludeFilters;
	}
}
